package agngui.ui;

import agngui.tools.*;

import javax.swing.*;

import java.util.*;
import java.util.List;

/**
 * Contains all of the information for a given region's configuration panel
 * 
 * 
 * @author Ben Bloom
 */
public class RegionConfigPanel extends JPanel {

	/**
	 * end of line character for the system
	 */
	public static String endl = System.getProperty("line.separator");
	
	/**
	 * the basic set of label strings to use
	 */
	protected static String[] label_strings = ConfigWindow.common_strings;
	
	/**
	 * the name of this region
	 */
	private String my_name;
	
	/**
	 * a mapping from a field's name to the actual field
	 */
    private Map<String, Field> fields;
    
    /**
     * a mapping from a field's name to the range associated with that field
     */
	private Map<Field, Range> ranges;
	
	/**
	 * the constraint object in use for this panel
	 */
	private Constraint constraint;
	
	/**
	 * the number of files that this panel will generate
	 */
	private int will_generate;
	
	/**
	 * constructs a new RegionConfigPanel
	 * 
	 * @param panel_name The name of the panel
	 * @param extra_label_strings Extra labels to create fields with
	 * @param use_base_strings True if the common base strings should be used, false if not
	 * @param upper_bounded An array denoting which fields are upper bounded, and which are not
	 * @param typed An array denoting the unit type of each field
	 */
    public RegionConfigPanel(String panel_name, String[] extra_label_strings, boolean use_base_strings, boolean[] upper_bounded, UNIT_TYPE[] typed) {
        super();
        
        //myPanel = new JPanel(new SpringLayout());
        setLayout(new SpringLayout());
		my_name = panel_name;
		//set the constraint to a stub for the moment
		constraint = new Constraint() {
			public String checkConstraints() { return null; }
		};
		fields = new HashMap<String, Field>();
		ranges = new HashMap<Field, Range>();
		will_generate = 1;
		
		int counter = 0;
		if(use_base_strings) {
			for(String name : label_strings) {
				String value_string = "";
				String unit_string = null;
				String upper_string = null;
				String step_string = null;
				if(typed[counter] != UNIT_TYPE.NONE) {
					unit_string = "";
				}
				if(upper_bounded[counter]) {
					upper_string = "";
					step_string = "";
				}
				Field field = new Field(name, this, upper_bounded[counter], typed[counter], value_string, upper_string, step_string, unit_string);
				fields.put(name.trim(), field);
				counter++;
			}
		}
		
		for(String name : extra_label_strings) {
			String value_string = "";
				String unit_string = null;
				String upper_string = null;
				String step_string = null;
				if(typed[counter] != UNIT_TYPE.NONE) {
					unit_string = "";
				}
				if(upper_bounded[counter]) {
					upper_string = "";
					step_string = "";
				}
				Field field = new Field(name, this, upper_bounded[counter], typed[counter], value_string, upper_string, step_string, unit_string);
				fields.put(name.trim(), field);
				counter++;
		}
		
		int rows = 0;
		int cols = 7;
		if(use_base_strings) {
			for(String name : label_strings) {
				String trimmed = name.trim();
				JComponent[] components = fields.get(trimmed).generateComponents();
				for(JComponent component : components) {
					JPanel panel = new JPanel();
					panel.add(component);
					add(panel);
				}
				rows++;
			}
		}
		for(String name : extra_label_strings) {
			String trimmed = name.trim();
			JComponent[] components = fields.get(trimmed).generateComponents();
			for(JComponent component : components) {
				JPanel panel = new JPanel();
				panel.add(component);
				add(panel);
			}
			rows++;
		}
		SpringUtilities.makeCompactGrid(this, rows, cols, 0, 0, 1, 1);
		//add(myPanel);
		//add(new JScrollPane(this));
    }
	
    /**
     * verifies the numbers in each field
     * 
     * @throws IllegalArgumentException if the fields do not contain valid numbers
     */
	public void verifyFields() throws IllegalArgumentException {
		Collection<Field> values = fields.values();
		for(Field field : values) {
			try {
				if(field.getValue().indexOf(";") != -1) {
					field.verifyList();
				} else {
					field.verifyNumbers();
				}
			} catch(IllegalArgumentException iae) {
				field.grabFocus();
				throw new IllegalArgumentException(my_name + " :: " + iae.getMessage());
			}
		}
	}
	
	/**
	 * verifies and then converts all field values
	 * 
	 * @throws IllegalArgumentException if values are not verifiable
	 */
	public void doConversions() throws IllegalArgumentException {
		verifyFields();
		Collection<Field> values = fields.values();
		for(Field field : values) {
			try {
				field.convertUnits();
			} catch(IllegalArgumentException iae) {
				field.grabFocus();
				throw new IllegalArgumentException(my_name + " :: " + iae.getMessage());
			}
		}
	}
	
	/**
	 * creates ranges for the fields, and reports them back
	 * 
	 * @return a mapping from field names to ranges
	 */
	public Map<Field, Range> fillRanges() {
		will_generate = 1;
		Collection<Field> values = fields.values();
		
		for(Field field : values) {
			if(field.hasUpper()) {
				String lower_text = field.getValue();
				String upper_text = field.getUpper();
				String step_text = field.getStep();
				if(!lower_text.equals("") && !upper_text.equals("") && !step_text.equals("")) {
					double lower = Double.parseDouble(lower_text);
					double upper = Double.parseDouble(upper_text);
					double step = Double.parseDouble(step_text);
					Range range = new Range(lower, upper, step);
					will_generate *= range.numChunks();
					ranges.put(field, range);
				}
			}
		}
		return ranges;
	}
	
	/**
	 * gives the number of files that this panel will generate
	 * 
	 * @return the number of files that this panel will generate
	 */
	public int getNumFilesToGenerate() { return will_generate; }

	/**
	 * returns the field with the given label ID
	 * 
	 * @param label The name of the field to get
	 * @return The field associated with the given label
	 */
    public Field getField(String label) {
        return fields.get(label);
    }
	
    /**
     * gives the name of this region
     * 
     * @return the name of this region
     */
	public String getName() { return my_name; }
	
	/**
	 * returns a mapping from field names to the actual fields
	 * 
	 * @return a mapping from field names to fields themselves
	 */
	public Map<String, Field> getFieldMap() { return fields; }
	
	/**
	 * implements Constraint.checkConstraints()
	 * 
	 * 
	 * @return an error string denoting all errors in this panel
	 */
	public String checkConstraints() { 
		return constraint.checkConstraints(); 
	}
	
	/**
	 * sets the constraint object for this region
	 * @param c The new constraint object to use
	 */
	public void setConstraint(Constraint c) {
		this.constraint = c;
	}
	
	/**
	 * returns a mapping from field names to the field values in default units
	 * 
	 * @return a mapping from field names to values in default units
	 */
	public Map<String, Double> getValueMap() {
		Map<String, Double> value_map = new HashMap<String, Double>();
		Iterator<Map.Entry<String, Field>> itr = fields.entrySet().iterator();
		while(itr.hasNext()) {
			Map.Entry<String, Field> entry = itr.next();
			String name = entry.getKey();
			Field field = entry.getValue();
			try {
				field.verifyNumbers();
			} catch(IllegalArgumentException iae) {
				throw new IllegalArgumentException(my_name + " :: " + iae.getMessage());
			}
			
			double val = 0;
			//special case defaulting to non-SI-default units.
			if(name.equals(ConfigWindow.image_strings[0].trim())) {
				val = Double.parseDouble(field.getValue());
				val = Converter.convertUnits(val, field.getUnits(), "Mpc");
			} else if(name.equals(ConfigWindow.image_strings[1].trim())) {
				val = Double.parseDouble(field.getValue());
				val = Converter.convertUnits(val, field.getUnits(), "arcsec");
			} else if(name.equals(ConfigWindow.wavelength_strings[0].trim())) {
				val = Double.parseDouble(field.getValue());
				val = Converter.convertUnits(val, field.getUnits(), "a0");
			} else if(name.equals(ConfigWindow.wavelength_strings[1].trim())) {
				val = Double.parseDouble(field.getValue());
				val = Converter.convertUnits(val, field.getUnits(), "a0");
			} else if(name.equals(ConfigWindow.wavelength_strings[2].trim())) {
				val = Double.parseDouble(field.getValue());
				val = Converter.convertUnits(val, field.getUnits(), "a0");
			}	else {
				val = Double.parseDouble(field.getValueInDefaultUnits());
			}
			value_map.put(name, val);
		}
		
		return value_map;
	}
	
	/**
	 * returns a string which can be used for saving the system configuration for
	 * this panel's contribution to the system.
	 * 
	 * @return a string that denotes the configuration of this panel, to be used for saving configurations
	 */
	public String getConfigurationString() {
		Iterator<Map.Entry<String, Field>> itr = fields.entrySet().iterator();
		StringBuilder buf = new StringBuilder("");
		while(itr.hasNext()) {
			Map.Entry<String, Field> entry = itr.next();
			
			String name = entry.getKey();
			Field field = entry.getValue();
			
			buf.append(name + " " + field.getValue() + endl);
			if(field.hasUpper()) {
				buf.append("+Upper Bound: " + field.getUpper() + endl
				         + "+Step Size: " + field.getStep() + endl);
			}
			if(field.hasUnits()) {
				buf.append("+Units: " + field.getUnits() + endl);
			}
		}
		
		return buf.toString();
	}
	
	/**
	 * loads the information for this panel from the given list of information
	 * 
	 * @param entries The field names and parameters to use for this panel
	 */
	public void loadConfiguration(List<String> entries) {
		int size = entries.size();
		//fields.clear();
		for(int i = 0; i < size; i++) {
			String name = entries.get(i);
			String value = name.substring(name.indexOf(":")+1).trim();
			name = name.substring(0, name.indexOf(":")+1).trim();
			//System.out.println("." + name + ".");
			String next_line = null;
			String upper = null;
			String step = null;
			String units = null;
			boolean has_upper = false;
			UNIT_TYPE type = UNIT_TYPE.NONE;
			if(i+1 < size) {
				next_line = entries.get(i+1);
				if(next_line.startsWith("+Upper")) {
					has_upper = true;
					upper = next_line.substring(next_line.indexOf(":")+1).trim();
					next_line = entries.get(i+2);
					step = next_line.substring(next_line.indexOf(":")+1).trim();
					i += 2;
					if(i+1 < size) {
						next_line = entries.get(i+1);
					}
				}
				
				if(next_line.startsWith("+Units")) {
					units = next_line.substring(next_line.indexOf(":")+1).trim();
					type = Field.getType(units);
					i++;
				}
			}

			fields.get(name).repopulate(has_upper, type, value, upper, step, units);
		}
	}
	
	/**
     * displays a dialog alert box with the given message
     * @param message The error message
     */
	public void error(String message) {
		JOptionPane.showMessageDialog(this, message, "Error!", JOptionPane.ERROR_MESSAGE);
	}
}
